import java.util.*;
/**
 * Piscina de hilos extremadamente simple (aunque funcional). Esta clase
 * solo pretende ilustrar el principio de funcionamiento de dicho patron.
 * Para uso practicos, empleese en su lugar la clase
 * java.util.concurrent.ThreadPoolExecutor y las interfaces y clases
 * relacionadas.
 *
 * @author Jose Antonio Perez Castellanos
 * @see java.util.concurrent.ThreadPoolExecutor
 */
public class PiscinaHilos
{

   // Cada uno de los hilos en la piscina sera una instancia de esta clase:
   // de esta clase anidada:
    protected class Trabajador extends Thread
    {
        private PiscinaHilos piscina;    // Piscina a la que pertenece el trabajador
        public Trabajador (PiscinaHilos piscina)
        {
            this.piscina= piscina;
        }
        public void run()
        {
           // Dura vida del trabajador:
            while (true)
            {
                Runnable tarea= piscina.obtener_tarea();
                tarea.run();
                piscina.fin_tarea();
            }
        }
    }

private Trabajador trabajadores[];            // Hilos que constituyen la piscina
private Vector<Runnable> cola_tareas;       // Cola de tareas pendientes
private int tareas_en_ejecucion;            // Numero de tareas que han sido retiradas de la cola
                                            // por un hilo y aun no han terminado


/**
 * Retira una tarea de la cola de tareas. Este metodo sera utilizado por
 * los hilos de la pisicina cuando se encuentren ociosos para conseguir
 * una nueva tarea. Si no hay ninguna tarea pendiente, el hilo llamante
 * espera hasta la aparicion de una nueva tarea.
 *
 * @return Tarea mas antigua de la cola.
 */
protected synchronized Runnable obtener_tarea()
{
   // Si la cola de tareas pendientes esta vacia, esperamos a que aparezca una
    while (cola_tareas.isEmpty())
    try
    {
        wait();
    }catch(InterruptedException e) {e.printStackTrace();}

   // Vamos a comenzar la ejecucion de la tarea que hemos retirado
    tareas_en_ejecucion++;

   // Retiramos la tarea de la cola
    return cola_tareas.remove(0);
}

/**
 * Marca el final de una tarea. Este metodo sera utilizado por cada
 * hilo que termine una tarea para notificar a la piscina que hay
 * una tarea menos en ejecucion.
 */
protected synchronized void fin_tarea()
{
   // Una tarea ha terminado...
    tareas_en_ejecucion--;

   // ... lo cual puede cambiar la condicion de alguna espera guardada
    notifyAll();
}

/**
 * Crea una piscina de hilo con un determinado numero de hilos trabajadores.
 * Los hilos trabajadores comienzan su ejecucion desde el mismo momento de la
 * creacion.
 *
 * @param ntrabajadores        Numero de hilos que componen la piscina.
 */
public PiscinaHilos (int ntrabajadores)
{
    cola_tareas= new Vector<Runnable>();
    trabajadores= new Trabajador[ntrabajadores];
    tareas_en_ejecucion= 0;

   // Creamos los hilos de la piscina:
    for (int i=0; i<ntrabajadores; i++)
    {
        trabajadores[i]= new Trabajador(this);
        trabajadores[i].start();
    }
}

/**
 * Envia una nueva tarea a la piscina. La tarea no es mas que un objeto
 * que implementa la interfaz Runnable, metodo que es ejecutado por los
 * hilos que componen la piscina.
 *
 * @param tarea        Objeto Runnable con la logica a ejecutar.
 */
public synchronized void nuevaTarea (Runnable tarea)
{
   // Colocamos tarea en la cola de tareas
    cola_tareas.add(tarea);

   // Puede haber algun hilo ocioso esperando que esto ocurra,
   // por lo que avisamos:
    notifyAll();
}

/**
 * Espera hasta que la piscina haya quedado ociosa, es decir, hasta que
 * no haya ninguna tarea en la cola, y todos los hilos esten ociosos.
 */
public synchronized void esperarOciosa ()
{
    while (!(cola_tareas.isEmpty() && tareas_en_ejecucion == 0))
    try
    {
        wait();
    }catch(InterruptedException e) {e.printStackTrace();}
}

}
